﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CashFlow.Common;
using CashFlow.Entities;
using CashFlow.Repository;
using System.Diagnostics.Contracts;

namespace CashFlow.BusinessComponents
{
	public class CashFlowComponent
	{
		private string tenantId;
		private Tenant tenant;
		private IEnumerable<Bill> bills;
		private DateRange dateRange;
		private List<DateTime> months = new List<DateTime>();

		private ITenantRepository tenantRepository;
		private IBillRepository billRepository;

		private readonly DateTimeUtil dateUtil = new DateTimeUtil();

		public CashFlowComponent(string tenantId)
		{
			this.tenantId = tenantId;
			//tenentReposit
		}

		public CashFlowComponent(
			string tenantId, 
			ITenantRepository tenantRepository,
			IBillRepository billRepository)
		{
			Contract.Requires(!string.IsNullOrEmpty(tenantId), "Tenant id cannot be null.");
			Contract.Requires(tenantRepository != null, "Tenant Repository cannot be null.");
			Contract.Requires(billRepository != null, "Bill Repository cannot be null.");

			this.tenantId = tenantId;
			this.tenantRepository = tenantRepository;
			this.billRepository = billRepository;
		}

		public void GetCashFlow(){
			GetTenant();
			GetBills();
			//TODO: vvs p2 what if tenant.InitialBalanceDate > DateTime.UtcNow
			dateRange = new DateRange(tenant.InitialBalanceDate, DateTime.UtcNow);
			months = dateUtil.GetMonths(dateRange.MinDate, dateRange.MaxDate).ToList();
	
			var monthlyData = GetCashFlowEvents();
		}

		private IEnumerable<MonthCashFlow> GetCashFlowEvents()
		{
			List<MonthCashFlow> cashFlow = null;
			var numberOfMonths = months.Count();
			if (numberOfMonths > 0)
			{
				cashFlow = months.
					OrderByDescending(d => d).
					Select(m => GetMonthCashFlow(m, bills)).
					ToList();
			}
			//TODO: vvs p3 prev statement - converting to List, but can I do IEnumerable?
			return cashFlow;
		}

		public MonthCashFlow GetMonthCashFlow(DateTime date, IEnumerable<Bill> bills)
		{
			//TODO: vvs p2 this is for the whole month: ensure that date == first month date.
			var cashFlow = new MonthCashFlow(date);
			var dateRange = new DateRange(date, dateUtil.MonthLastDate(date));

			var cashFlowData = bills.SelectMany(
				bill => bill.Recurrence.GetNextDates(dateRange).
				Select(cashFlowDate => new CashFlowEvent {
						Date = cashFlowDate,
						Bill = bill
				})).
				OrderByDescending(cf => cf.Date);

			cashFlow.Items = cashFlowData;

			return cashFlow;
		}

		public void CalcBalances(MonthCashFlow cashFlow, DateTime startBalanceDate, double startBalance)
		{
			var balance = startBalance;
			if (startBalanceDate.Day == 1)
			{
				foreach (var item in cashFlow.Items)
				{
					item.Balance = balance + item.Bill.Amount;
					balance = item.Balance;
				}
				cashFlow.BeginingBalance = startBalance;
				cashFlow.EndingBalance = balance;
			}
			else
			{
				foreach (var item in cashFlow.Items.Where(i => i.Date >= startBalanceDate))
				{
					item.Balance = balance + item.Bill.Amount;
					balance = item.Balance;
				}
				cashFlow.EndingBalance = balance;

				balance = startBalance;
				foreach (var item in cashFlow.Items.Where(i => i.Date < startBalanceDate).OrderByDescending(d => d.Date))
				{
					item.Balance = balance - item.Bill.Amount;
					balance = item.Balance;
				}
				cashFlow.BeginingBalance = balance;
			}
		}

		private void GetBills()
		{
			bills = billRepository.GetAll();
		}

		private void GetTenant()
		{
			Contract.Ensures(tenant != null, "Tenant doesn't exist.");
			tenant = tenantRepository.GetById();
		}


		public void Calc()
		{
			//var bills = GetBills();
			//var billComponent = new BillComponent();
			//if (!HasBills(bills))
			//  return;

			//var dateTimeUtil = new DateTimeUtil();
			//var today = DateTime.Now; //Should get user time zone
			//var todayYearMonth = dateTimeUtil.ToYYMM(today);

			//Tuple<DateTime, double> initialBalance = 
			//  GetInitialBalance() ??
			//  new Tuple<DateTime, double>(dateTimeUtil.MonthFirstDate(today), 0);

			//IEnumerable<CashFlowEvent> cashFlow = billComponent.GetCashFlow(today);

			//cashFlow.
			//  Where(e => e.Date >= initialBalance.Item1).
			//  Aggregate((e, total) => {
			//    e.Balance = total.Balance + e.Bill.Amount;
			//    return e;
			//  });




		}
	}
}
